package binarization;

import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.imageio.ImageIO;

import util.Util;
import colorChanger.ColoredQRCode;
import colorChanger.ColoredQRCode2;
import colorChanger.RGBtoCMYK;
import colorChanger.RGBtoCMYK2;

public class OtsuBinarize {

	private static BufferedImage original, grayscale, binarized;

	public static void main(String[] args) throws IOException {

		File original_f = new File("eu" + ".jpg");
		String output_f = "bebe" + "_bin";
		original = ImageIO.read(original_f);
		grayscale = toGray(original);
		binarized = binarize(grayscale);
		writeImage(output_f);

	}

	private static void writeImage(String output) throws IOException {
		File file = new File(output + ".jpg");
		ImageIO.write(binarized, "jpg", file);
	}

	// Return histogram of grayscale image
	public static int[] imageHistogram(BufferedImage input) {

		int[] histogram = new int[256];

		for (int i = 0; i < histogram.length; i++)
			histogram[i] = 0;

		for (int i = 0; i < input.getWidth(); i++) {
			for (int j = 0; j < input.getHeight(); j++) {
				int red = new Color(input.getRGB(i, j)).getRed();
				histogram[red]++;
			}
		}

		return histogram;

	}

	// The luminance method
	public static BufferedImage toGray(BufferedImage original) {

		int alpha, red, green, blue;
		int newPixel;

		BufferedImage lum = new BufferedImage(original.getWidth(),
				original.getHeight(), original.getType());
		for (int i = 0; i < original.getWidth(); i++) {
			for (int j = 0; j < original.getHeight(); j++) {

				// Get pixels by R, G, B
				alpha = new Color(original.getRGB(i, j)).getAlpha();
				red = new Color(original.getRGB(i, j)).getRed();
				green = new Color(original.getRGB(i, j)).getGreen();
				blue = new Color(original.getRGB(i, j)).getBlue();

				red = (int) (0.21 * red + 0.71 * green + 0.07 * blue);
				newPixel = colorToRGB(alpha, red, red, red);

				// Write pixels into image
				lum.setRGB(i, j, newPixel);

			}
		}

		return lum;

	}

	// Get binary treshold using Otsu's method
	private static int otsuTreshold(BufferedImage original) {

		int[] histogram = imageHistogram(original);
		int total = original.getHeight() * original.getWidth();

		float sum = 0;
		for (int i = 0; i < 256; i++)
			sum += i * histogram[i];

		float sumB = 0;
		int wB = 0;
		int wF = 0;

		float varMax = 0;
		int threshold = 0;

		for (int i = 0; i < 256; i++) {
			wB += histogram[i];
			if (wB == 0)
				continue;
			wF = total - wB;

			if (wF == 0)
				break;

			sumB += (float) (i * histogram[i]);
			float mB = sumB / wB;
			float mF = (sum - sumB) / wF;

			float varBetween = (float) wB * (float) wF * (mB - mF) * (mB - mF);

			if (varBetween > varMax) {
				varMax = varBetween;
				threshold = i;
			}
		}

		return threshold;

	}

	public static BufferedImage binarize(BufferedImage original) {

		int red;
		int newPixel;

		int threshold = otsuTreshold(original);

		BufferedImage binarized = new BufferedImage(original.getWidth(),
				original.getHeight(), original.getType());

		for (int i = 0; i < original.getWidth(); i++) {
			for (int j = 0; j < original.getHeight(); j++) {

				// Get pixels
				red = new Color(original.getRGB(i, j)).getRed();
				int alpha = new Color(original.getRGB(i, j)).getAlpha();
				if (red > threshold) {
					newPixel = 255;
				} else {
					newPixel = 0;
				}
				// if(colorToRGB(alpha, newPixel, newPixel,
				// newPixel)==RGBtoCMYK.WHITE_PIXEL){
				// newPixel=RGBtoCMYK.WHITE_PIXEL;
				// }else{
				// newPixel=original.getRGB(i, j);
				// }
				newPixel = colorToRGB(alpha, newPixel, newPixel, newPixel);
				// System.out.println(newPixel);
				binarized.setRGB(i, j, newPixel);

			}
		}

		return binarized;

	}

	public static BufferedImage binarizeToPrimary(BufferedImage original)
			throws FileNotFoundException, IOException {
		Util.writhImage(original,
				"Images/Recognition/primaria-0passo-imagemOriginal");

		// first step
		BufferedImage binarized = primaryBunarization(original);
		Util.writhImage(binarized,
				"Images/Recognition/primaria-1passo-binarizada");

		// second step
		BufferedImage secundaryImage = cyanToBlack(binarized);
		Util.writhImage(secundaryImage, "Images/Recognition/primaria-2passo");

		return secundaryImage;

	}

	public static BufferedImage binarizeToSecundary(BufferedImage original)
			throws FileNotFoundException, IOException {

		Util.writhImage(original,
				"Images/Recognition/secundaria-0passo-imagemOriginal");
		// first step
		// List<Integer> alteredPixelI = new ArrayList<Integer>();
		// List<Integer> alteredPixelJ = new ArrayList<Integer>();
		BufferedImage binarized = binarizeSecundary(original);
		Util.writhImage(binarized,
				"Images/Recognition/secundaria-1passo-imagemBinarizada");
		// magenta

		// second step
		BufferedImage magentaImage = magentaToBlack(binarized);
		Util.writhImage(magentaImage,
				"Images/Recognition/secundaria-2passo-identificarMagenta");

		// third step
		BufferedImage secundaryQRCODE = mergeBlackWithMagenta(original,
				magentaImage);
		Util.writhImage(secundaryQRCODE,
				"Images/Recognition/secundaria-3passo-final");

		return secundaryQRCODE;

	}

	private static BufferedImage cyanToBlack(BufferedImage binarized)
			throws FileNotFoundException, IOException {
		return Util.imageToBufferedImage(new ColoredQRCode(binarized)
				.getSecundaryQRCode().getImageFromPixelArray());
	}

	private static BufferedImage primaryBunarization(BufferedImage original) {
		int threshold = otsuTreshold(original);
		int red;
		int newPixel;
		BufferedImage binarized = new BufferedImage(original.getWidth(),
				original.getHeight(), original.getType());

		for (int i = 0; i < original.getWidth(); i++) {
			for (int j = 0; j < original.getHeight(); j++) {

				// Get pixels
				red = new Color(original.getRGB(i, j)).getRed();
				int alpha = new Color(original.getRGB(i, j)).getAlpha();
				if (red > threshold) {
					newPixel = 255;
				} else {
					newPixel = 0;
				}
				if (colorToRGB(alpha, newPixel, newPixel, newPixel) == RGBtoCMYK.WHITE_PIXEL) {
					newPixel = RGBtoCMYK.WHITE_PIXEL;
				} else {
					newPixel = original.getRGB(i, j);
				}
				binarized.setRGB(i, j, newPixel);

			}
		}
		return binarized;
	}

	private static BufferedImage mergeBlackWithMagenta(BufferedImage original,
			BufferedImage onlyMagenta) {
		BufferedImage binarized = original;
		for (int i = 0; i < original.getWidth(); i++) {
			for (int j = 0; j < original.getHeight(); j++) {
				if (RGBtoCMYK2.KEY_PIXEL == onlyMagenta.getRGB(i, j)) {
					binarized.setRGB(i, j, RGBtoCMYK2.KEY_PIXEL);
				} else if (RGBtoCMYK2.isKey(original.getRGB(i, j))) {
					binarized.setRGB(i, j, RGBtoCMYK2.KEY_PIXEL);
				} else {
					binarized.setRGB(i, j, RGBtoCMYK2.WHITE_PIXEL);
				}
			}
		}
		return binarized;
	}

	private static BufferedImage binarizeSecundary(BufferedImage original) {
		int threshold = otsuTreshold(original);
		BufferedImage binarized = new BufferedImage(original.getWidth(),
				original.getHeight(), original.getType());
		int red;
		int newPixel;
		for (int i = 0; i < original.getWidth(); i++) {
			for (int j = 0; j < original.getHeight(); j++) {

				// Get pixels
				red = new Color(original.getRGB(i, j)).getRed();
				int alpha = new Color(original.getRGB(i, j)).getAlpha();
				if (red > threshold) {
					newPixel = 255;
				} else {
					newPixel = 0;
				}
				if (colorToRGB(alpha, newPixel, newPixel, newPixel) == RGBtoCMYK.WHITE_PIXEL) {
					// newPixel = RGBtoCMYK.WHITE_PIXEL;
					newPixel = original.getRGB(i, j);

					// alteredPixelI.add(i);
					// alteredPixelJ.add(j);
				} else {
					newPixel = RGBtoCMYK.WHITE_PIXEL;
				}
				binarized.setRGB(i, j, newPixel);

			}

		}
		return binarized;
	}

	private static BufferedImage magentaToBlack(BufferedImage binarized)
			throws FileNotFoundException, IOException {
		return Util.imageToBufferedImage(new ColoredQRCode2(binarized)
				.getPrimaryQRCode().getImageFromPixelArray());
	}

	// Convert R, G, B, Alpha to standard 8 bit
	private static int colorToRGB(int alpha, int red, int green, int blue) {

		int newPixel = 0;
		newPixel += alpha;
		newPixel = newPixel << 8;
		newPixel += red;
		newPixel = newPixel << 8;
		newPixel += green;
		newPixel = newPixel << 8;
		newPixel += blue;

		return newPixel;

	}

}